#pragma once

#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <tbb/concurrent_unordered_map.h>
#include "thread.h"
#include "fs_types.h"

using namespace std;
// Note: 
// 1. any type of pointer/reference parameters should start with any prefix of const, OUT or INOUT.
// 2. callee should guarantee that: 
//    a) if the end time of operation A is before the begin time of operation B, A should always get the status before B (exclude B's effect).
//    b) if the begin time of operation A is after the end time of operation B, A should always get the status after B (include B's effect, if B is successful).
//    c) otherwise, i.e. the execution time of A and B is overlapped, A could either include or exclude B's effect (decided by callee).
// 3. callee could return E_RETRY_LATER if the operation will be blocked by another a long time. (For example, appending big data and can't append another concurrently.)
//    And then, caller should retry this operation later. (For example, caller can enqueue this operation again, and invoke other operations firstly.)

const char* s_dirNames[] = {"abc", "def", "ghi"};
const char* s_fileNames[] = {"f1", "f2", "f3"};
const size_t s_count = 3;

class TestFSOperator 
{
	typedef tbb::concurrent_unordered_map<KEY_INFO, DIR_INFO>  ConcurrentDirInfoMap;
	typedef tbb::concurrent_unordered_map<KEY_INFO, FILE_INFO> ConcurrentFileInfoMap;

	ConcurrentDirInfoMap  _dirInfoMap;
	ConcurrentFileInfoMap _fileInfoMap;

public:
	TestFSOperator() 
	{ 
		USER_ID userID = 0;
		for (size_t i = 0; i < s_count; ++i)
		{
			DIR_INFO dirInfo(DIR_ID(userID, 0), s_dirNames[i]);
			dirInfo._dirID = DIR_ID(userID, (DIR_SN)(100 + i));
			_dirInfoMap.insert(ConcurrentDirInfoMap::value_type(dirInfo, dirInfo));
		}
		for (size_t i = 0; i < s_count; ++i)
		{
			FILE_INFO fileInfo(DIR_ID(userID, 0), s_fileNames[i]);
			_fileInfoMap.insert(ConcurrentFileInfoMap::value_type(fileInfo, fileInfo));
		}
	}

	/* Read Operations */
	HResult GetSubdirInfo(DIR_ID parentID, const string& dirName, OUT DIR_INFO& dirInfo) const
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << dirName << ")\n";
		std::cout << s.str();

		std::this_thread::yield();

		KEY_INFO key(parentID, dirName);
		auto it = _dirInfoMap.find(key);

		std::this_thread::yield();

		if (it == _dirInfoMap.end())
			return E_ITEM_NOT_FOUND;
		dirInfo = it->second;
		return S_OK;
	}

	HResult GetSubdirInfos(DIR_ID parentID, OUT std::vector<DIR_INFO>& dirInfos) const
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ")\n";
		std::cout << s.str();

		std::this_thread::yield();

		dirInfos.clear();
		for (auto it = _dirInfoMap.begin(); it != _dirInfoMap.end(); ++it)
		{
			if (it->first._parentID == parentID)
				dirInfos.push_back(it->second);
		}

		std::this_thread::yield();
		return S_OK;
	}

	HResult GetSubfileInfo(DIR_ID parentID, const string& fileName, OUT FILE_INFO& fileInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << fileName << ")\n";
		std::cout << s.str();

		std::this_thread::yield();

		KEY_INFO key(parentID, fileName);
		auto it = _fileInfoMap.find(key);
		std::this_thread::yield();
		if (it == _fileInfoMap.end())
			return E_ITEM_NOT_FOUND;
		fileInfo = it->second;
		return S_OK;
	}

	HResult GetSubfileInfos(DIR_ID parentID, OUT std::vector<FILE_INFO>& fileInfos) const
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ")\n";
		std::cout << s.str();

		std::this_thread::yield();

		fileInfos.clear();
		for (auto it = _fileInfoMap.begin(); it != _fileInfoMap.end(); ++it)
		{
			if (it->first._parentID == parentID)
				fileInfos.push_back(it->second);
		}

		std::this_thread::yield();
		return S_OK;
	}

	HResult ReadFile(const FILE_INFO& sourceInfo, uint64_t offset, OUT uint8_t* buf, size_t readCount, OUT size_t& actualReadCount) const
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << sourceInfo.GetKey() << ", " << offset << ", " << readCount << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	/* Write Operations */
	HResult CreateNewDir(DIR_ID parentID, const string& newDirName, OUT DIR_INFO& newDirInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << newDirName << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult CreateLinkDir(DIR_ID parentID, const string& newDirName, DIR_ID sourceID, OUT DIR_INFO& newDirInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << newDirName << ", " << sourceID._id << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult RenameDir(DIR_ID parentID, const string& orgDirName, const string& newDirName, OUT DIR_INFO& newDirInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << newDirName << ", " << newDirName << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult DeleteDir(DIR_ID parentID, const string& orgDirName)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << orgDirName << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult CreateNewFile(DIR_ID parentID, const string& newFileName, const uint8_t* buf, size_t bufLen, OUT FILE_INFO& newFileInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << newFileName << ", " << buf << ", " << bufLen << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult CreateNewFile(DIR_ID parentID, const string& newFileName, const string& localFileName, OUT FILE_INFO& newFileInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << newFileName << ", " << localFileName << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult CreateLinkFile(DIR_ID parentID, const string& newFileName, const FILE_INFO& sourceInfo, OUT FILE_INFO& newFileInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << newFileName << ", " << sourceInfo.GetKey() << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult RenameFile(DIR_ID parentID, const string& orgFileName, const string& newFileName, OUT FILE_INFO& newFileInfo)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << orgFileName << ", " << newFileName << ")\n";
		std::cout << s.str();
		return S_OK;
	}

	HResult DeleteFile(DIR_ID parentID, const string& orgFileName)
	{
		std::ostringstream s;
		s << __FUNCTION__ << '(' << parentID._id << ", " << orgFileName << ")\n";
		std::cout << s.str();
		return S_OK;
	}
};
